package suites.queue {
	import suites.queue.mock.AsyncQable;

	import org.flexunit.asserts.assertEquals;
	import org.flexunit.asserts.assertNotNull;
	import org.flexunit.asserts.assertNull;
	import org.flexunit.asserts.assertTrue;
	import org.flexunit.asserts.fail;
	import org.splink.library.queue.IQ;
	import org.splink.library.queue.IQable;
	import org.splink.library.queue.Q;
	import org.splink.library.queue.QEvent;

	import flash.events.TimerEvent;
	import flash.utils.Timer;

	/**
	 * @author Max Kugland
	 */
	public class QRemoveTest extends AbstractQTest {

		[Test]
		public function remove() : void {
			_q.add(new AsyncQable("1"));			_q.add(new AsyncQable("2"));			_q.add(new AsyncQable("3"));
			_q.remove(_q.getForId("2"));
			assertEquals(2, _q.length);
			_q.remove(_q.getAt(0));
			assertEquals(1, _q.length);
		}

		[Test]
		public function removeNested() : void {
			_q.add(createQ(2));
			var q1 : IQ = createQ(2);
			q1.add(createQ(1));
			_q.remove(Q.getForId(_q, "i-1"));			_q.remove(_q.getForId("q-1"));
			_q.remove(_q.getAt(0));			assertEquals(0, _q.length);
		}

		[Test (async)]
		public function removeRunning() : void {
			handleAsync(1500);
			_q.add(new AsyncQable("1"));
			_q.add(new AsyncQable("2")).register(QEvent.COMPLETE, function(e : QEvent):void {
				_q.remove(_q.getForId("1"));				_q.remove(_q.getForId("2"));
				assertEquals(_q.getForId("3"), _q.getAt(0));
				assertEquals(1, _q.length);
			});
			_q.add(new AsyncQable("3"));
			_q.start();
		}

		[Test (async)]
		public function removeNestedRunning() : void {
			handleAsync(1500);

			var q : IQ = createQ(1); //q0
			var q1 : IQ = createQ(2);//q2			var q2 : IQ = createQ(1);//q5			var q3 : IQ = createQ(2);//q7
			q.add(q1).register(QEvent.COMPLETE, function(e : QEvent):void {
				var item : IQable = Q.getForId(_q, "q-5");
				assertNotNull(item);
				q.remove(item);
				assertNull(Q.getForId(_q, "q-5"));
			});
			
			_q.add(q);
			q.add(q2).register(QEvent.COMPLETE, function(e : QEvent):void {
				fail("Should have been removed");
			});
			q2.add(q3);
			
			_q.start();
		}

		[Test (async)]
		public function removeCurrentRunning() : void {
			handleAsync(1500);
			
			var q : IQ = createQ(3);
			q.getAt(1).register(QEvent.COMPLETE, function(e : QEvent) :void {
				assertNotNull(_q.current);
				_q.remove(_q.current);
			});			var q1 : IQ = createQ(1);
			q.add(q1);
			q.add(new AsyncQable());
			
			_q.add(q);
			_q.start();
		}
		
		[Test (async)]
		public function randomTimeRemove() : void {
			handleAsync(2000);
			
			var q : IQ = createQ(1); //q0
			var q1 : IQ = createQ(3);//q2
			var q2 : IQ = createQ(1);//q5
			var q3 : IQ = createQ(3);//q7			var q4 : IQ = createQ(2);//q10
			q.add(q1);
			q1.add(q2);			q1.add(q3);
			q3.add(q4);
			_q.add(q);
			
			var last : int = Q.getTotal(_q);
			var t : Timer = new Timer(20 + Math.random() * 50, 10);
			var timerFnc : Function = function(e : TimerEvent): void {
				var count : int = 0;
				var ran : int = Math.random() * Q.getTotal(_q);
				Q.visit(_q, function(qable : IQable):void {
					if(count == ran) {
//						trace("remove " + count+", " + getQForQable(_q, qable).id+" - " + qable.id);
						var parent : IQ = getQForQable(_q, qable);
						parent.remove(qable);
						assertNull(parent.getForId(qable.id));
						assertTrue(last > Q.getTotal(_q));
						last = Q.getTotal(_q);
					}
					count++;
				}); 
			};
			t.addEventListener(TimerEvent.TIMER, timerFnc);
			
			_q.start();
			_q.register(QEvent.COMPLETE, function(e : QEvent):void {
				t.removeEventListener(TimerEvent.TIMER, timerFnc);
			}, null, 1000);
			t.start();
		}
		
		public static function getQForQable(q : IQ, qable : IQable):IQ {
			if(q.getForId(qable.id)) {
				return q;
			} else {
				for (var i : int = 0; i < q.length; i++) {
					var next : IQable = q.getAt(i);
					if(next is IQ) {
						var r : IQ = getQForQable(IQ(next), qable);
						if(r) return r;
					}
				}
			}
			return null;
		}
	}
}
